home *** CD-ROM | disk | FTP | other *** search
/ Hardcore Gamer Resource Kit / Hardcore Gamer Resource Kit - Disc 2.iso / Utils / UNIX / UNZIP520 / MSDOS / MSDOS.C < prev    next >
C/C++ Source or Header  |  1996-04-23  |  54KB  |  1,559 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.   msdos.c
  4.  
  5.   MSDOS-specific routines for use with Info-ZIP's UnZip 5.2 and later.
  6.  
  7.   Contains:  Opendir()                      (from zip)
  8.              Readdir()                      (from zip)
  9.              do_wild()
  10.              mapattr()
  11.              mapname()
  12.              map2fat()
  13.              checkdir()
  14.              isfloppy()
  15.              volumelabel()                  (non-djgpp, non-emx)
  16.              close_outfile()
  17.              dateformat()
  18.              version()
  19.              _dos_getcountryinfo()          (djgpp, emx)
  20.             [_dos_getftime()                (djgpp, emx)   to be added]
  21.              _dos_setftime()                (djgpp, emx)
  22.              _dos_setfileattr()             (djgpp, emx)
  23.              _dos_getdrive()                (djgpp, emx)
  24.              _dos_creat()                   (djgpp, emx)
  25.              _dos_close()                   (djgpp, emx)
  26.              volumelabel()                  (djgpp, emx)
  27.              __crt0_glob_function()         (djgpp 2.x)
  28.              __crt_load_environment_file()  (djgpp 2.x)
  29.  
  30.   ---------------------------------------------------------------------------*/
  31.  
  32.  
  33.  
  34. #define UNZIP_INTERNAL
  35. #include "unzip.h"
  36.  
  37. #ifndef USE_VFAT
  38. static void map2fat OF((char *pathcomp, char *last_dot));
  39. #endif
  40. static int isfloppy OF((int nDrive));
  41. static int volumelabel OF((char *newlabel));
  42.  
  43. static int created_dir;        /* used by mapname(), checkdir() */
  44. static int renamed_fullpath;   /* ditto */
  45. static unsigned nLabelDrive;   /* ditto, plus volumelabel() */
  46.  
  47. /*****************************/
  48. /*  Strings used in msdos.c  */
  49. /*****************************/
  50.  
  51. #ifndef SFX
  52.   static char Far CantAllocateWildcard[] =
  53.     "warning:  can't allocate wildcard buffers\n";
  54. #endif
  55. static char Far Creating[] = "   creating: %s\n";
  56. static char Far ConversionFailed[] = "mapname:  conversion of %s failed\n";
  57. static char Far Labelling[] = "labelling %c: %-22s\n";
  58. static char Far ErrSetVolLabel[] = "mapname:  error setting volume label\n";
  59. static char Far PathTooLong[] = "checkdir error:  path too long: %s\n";
  60. static char Far CantCreateDir[] = "checkdir error:  can't create %s\n\
  61.                  unable to process %s.\n";
  62. static char Far DirIsntDirectory[] =
  63.   "checkdir error:  %s exists but is not directory\n\
  64.                  unable to process %s.\n";
  65. static char Far PathTooLongTrunc[] =
  66.   "checkdir warning:  path too long; truncating\n                   %s\n\
  67.                 -> %s\n";
  68. #if (!defined(SFX) || defined(SFX_EXDIR))
  69.    static char Far CantCreateExtractDir[] =
  70.      "checkdir:  can't create extraction directory: %s\n";
  71. #endif
  72. #ifdef __TURBOC__
  73.    static char Far AttribsMayBeWrong[] =
  74.      "\nwarning:  file attributes may not be correct\n";
  75. #endif
  76.  
  77. #ifdef WATCOMC_386
  78. #  define WREGS(v,r) (v##.w.##r)
  79. #  define int86x int386x
  80. #else
  81. #  define WREGS(v,r) (v##.x.##r)
  82. #endif
  83.  
  84. #if (defined(__GO32__) || defined(__EMX__))
  85. #  include <dirent.h>        /* use readdir() */
  86. #  define MKDIR(path,mode)   mkdir(path,mode)
  87. #  define Opendir  opendir
  88. #  define Readdir  readdir
  89. #  define Closedir closedir
  90. #  define zdirent  dirent
  91. #  define zDIR     DIR
  92. #  ifdef __EMX__
  93. #    include <dos.h>
  94. #    define GETDRIVE(d)      d = _getdrive()
  95. #    define FA_LABEL         A_LABEL
  96. #  else
  97. #    define GETDRIVE(d)      _dos_getdrive(&d)
  98. #  endif
  99. #else /* !(__GO32__ || __EMX__) */
  100. #  define MKDIR(path,mode)   mkdir(path)
  101. #  ifdef __TURBOC__
  102. #    define FATTR            FA_HIDDEN+FA_SYSTEM+FA_DIREC
  103. #    define FVOLID           FA_VOLID
  104. #    define FFIRST(n,d,a)    findfirst(n,(struct ffblk *)d,a)
  105. #    define FNEXT(d)         findnext((struct ffblk *)d)
  106. #    define GETDRIVE(d)      d=getdisk()+1
  107. #    include <dir.h>
  108. #  else /* !__TURBOC__ */
  109. #    define FATTR            _A_HIDDEN+_A_SYSTEM+_A_SUBDIR
  110. #    define FVOLID           _A_VOLID
  111. #    define FFIRST(n,d,a)    _dos_findfirst(n,a,(struct find_t *)d)
  112. #    define FNEXT(d)         _dos_findnext((struct find_t *)d)
  113. #    define GETDRIVE(d)      _dos_getdrive(&d)
  114. #    include <direct.h>
  115. #  endif /* ?__TURBOC__ */
  116.    typedef struct zdirent {
  117.        char d_reserved[30];
  118.        char d_name[13];
  119.        int d_first;
  120.    } zDIR;
  121.    zDIR *Opendir OF((const char *));
  122.    struct zdirent *Readdir OF((zDIR *));
  123. #  define Closedir free
  124.  
  125.  
  126.  
  127.  
  128. #ifndef SFX
  129.  
  130. /**********************/   /* Borland C++ 3.x has its own opendir/readdir */
  131. /* Function Opendir() */   /*  library routines, but earlier versions don't, */
  132. /**********************/   /*  so use ours regardless */
  133.  
  134. zDIR *Opendir(name)
  135.     const char *name;        /* name of directory to open */
  136. {
  137.     zDIR *dirp;              /* malloc'd return value */
  138.     char *nbuf;              /* malloc'd temporary string */
  139.     int len = strlen(name);  /* path length to avoid strlens and strcats */
  140.  
  141.  
  142.     if ((dirp = (zDIR *)malloc(sizeof(zDIR))) == (zDIR *)NULL)
  143.         return (zDIR *)NULL;
  144.     if ((nbuf = malloc(len + 5)) == (char *)NULL) {
  145.         free(dirp);
  146.         return (zDIR *)NULL;
  147.     }
  148.     strcpy(nbuf, name);
  149.     if (nbuf[len-1] == ':') {
  150.         nbuf[len++] = '.';
  151.     } else if (nbuf[len-1] == '/' || nbuf[len-1] == '\\')
  152.         --len;
  153.     strcpy(nbuf+len, "/*.*");
  154.     Trace((stderr, "Opendir:  nbuf = [%s]\n", nbuf));
  155.  
  156.     if (FFIRST(nbuf, dirp, FATTR)) {
  157.         free((zvoid *)nbuf);
  158.         return (zDIR *)NULL;
  159.     }
  160.     free((zvoid *)nbuf);
  161.     dirp->d_first = 1;
  162.     return dirp;
  163. }
  164.  
  165.  
  166.  
  167.  
  168.  
  169. /**********************/
  170. /* Function Readdir() */
  171. /**********************/
  172.  
  173. struct zdirent *Readdir(d)
  174.     zDIR *d;        /* directory stream from which to read */
  175. {
  176.     /* Return pointer to first or next directory entry, or NULL if end. */
  177.  
  178.     if (d->d_first)
  179.         d->d_first = 0;
  180.     else
  181.         if (FNEXT(d))
  182.             return (struct zdirent *)NULL;
  183.     return (struct zdirent *)d;
  184. }
  185.  
  186. #endif /* !SFX */
  187. #endif /* ?(__GO32__ || __EMX__) */
  188.  
  189.  
  190.  
  191.  
  192.  
  193. #ifndef SFX
  194.  
  195. /************************/
  196. /*  Function do_wild()  */   /* identical to OS/2 version */
  197. /************************/
  198.  
  199. char *do_wild(__G__ wildspec)
  200.     __GDEF
  201.     char *wildspec;          /* only used first time on a given dir */
  202. {
  203.     static zDIR *dir = (zDIR *)NULL;
  204.     static char *dirname, *wildname, matchname[FILNAMSIZ];
  205.     static int firstcall=TRUE, have_dirname, dirnamelen;
  206.     struct zdirent *file;
  207.  
  208.  
  209.     /* Even when we're just returning wildspec, we *always* do so in
  210.      * matchname[]--calling routine is allowed to append four characters
  211.      * to the returned string, and wildspec may be a pointer to argv[].
  212.      */
  213.     if (firstcall) {        /* first call:  must initialize everything */
  214.         firstcall = FALSE;
  215.  
  216.         if (!iswild(wildspec)) {
  217.             strcpy(matchname, wildspec);
  218.             have_dirname = FALSE;
  219.             dir = NULL;
  220.             return matchname;
  221.         }
  222.  
  223.         /* break the wildspec into a directory part and a wildcard filename */
  224.         if ((wildname = strrchr(wildspec, '/')) == (char *)NULL &&
  225.             (wildname = strrchr(wildspec, ':')) == (char *)NULL) {
  226.             dirname = ".";
  227.             dirnamelen = 1;
  228.             have_dirname = FALSE;
  229.             wildname = wildspec;
  230.         } else {
  231.             ++wildname;     /* point at character after '/' or ':' */
  232.             dirnamelen = (int)(wildname - wildspec);
  233.             if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) {
  234.                 Info(slide, 1, ((char *)slide,
  235.                   LoadFarString(CantAllocateWildcard)));
  236.                 strcpy(matchname, wildspec);
  237.                 return matchname;   /* but maybe filespec was not a wildcard */
  238.             }
  239. /* GRR:  can't strip trailing char for opendir since might be "d:/" or "d:"
  240.  *       (would have to check for "./" at end--let opendir handle it instead) */
  241.             strncpy(dirname, wildspec, dirnamelen);
  242.             dirname[dirnamelen] = '\0';       /* terminate for strcpy below */
  243.             have_dirname = TRUE;
  244.         }
  245.         Trace((stderr, "do_wild:  dirname = [%s]\n", dirname));
  246.  
  247.         if ((dir = Opendir(dirname)) != (zDIR *)NULL) {
  248.             while ((file = Readdir(dir)) != (struct zdirent *)NULL) {
  249.                 Trace((stderr, "do_wild:  readdir returns %s\n", file->d_name));
  250.                 if (match(file->d_name, wildname, 1)) {  /* 1 == ignore case */
  251.                     Trace((stderr, "do_wild:  match() succeeds\n"));
  252.                     if (have_dirname) {
  253.                         strcpy(matchname, dirname);
  254.                         strcpy(matchname+dirnamelen, file->d_name);
  255.                     } else
  256.                         strcpy(matchname, file->d_name);
  257.                     return matchname;
  258.                 }
  259.             }
  260.             /* if we get to here directory is exhausted, so close it */
  261.             Closedir(dir);
  262.             dir = (zDIR *)NULL;
  263.         }
  264.         Trace((stderr, "do_wild:  Opendir(%s) returns NULL\n", dirname));
  265.  
  266.         /* return the raw wildspec in case that works (e.g., directory not
  267.          * searchable, but filespec was not wild and file is readable) */
  268.         strcpy(matchname, wildspec);
  269.         return matchname;
  270.     }
  271.  
  272.     /* last time through, might have failed opendir but returned raw wildspec */
  273.     if (dir == (zDIR *)NULL) {
  274.         firstcall = TRUE;  /* nothing left to try--reset for new wildspec */
  275.         if (have_dirname)
  276.             free(dirname);
  277.         return (char *)NULL;
  278.     }
  279.  
  280.     /* If we've gotten this far, we've read and matched at least one entry
  281.      * successfully (in a previous call), so dirname has been copied into
  282.      * matchname already.
  283.      */
  284.     while ((file = Readdir(dir)) != (struct zdirent *)NULL)
  285.         if (match(file->d_name, wildname, 1)) {   /* 1 == ignore case */
  286.             if (have_dirname) {
  287.                 /* strcpy(matchname, dirname); */
  288.                 strcpy(matchname+dirnamelen, file->d_name);
  289.             } else
  290.                 strcpy(matchname, file->d_name);
  291.             return matchname;
  292.         }
  293.  
  294.     Closedir(dir);     /* have read at least one dir entry; nothing left */
  295.     dir = (zDIR *)NULL;
  296.     firstcall = TRUE;  /* reset for new wildspec */
  297.     if (have_dirname)
  298.         free(dirname);
  299.     return (char *)NULL;
  300.  
  301. } /* end function do_wild() */
  302.  
  303. #endif /* !SFX */
  304.  
  305.  
  306.  
  307.  
  308.  
  309. /**********************/
  310. /* Function mapattr() */
  311. /**********************/
  312.  
  313. int mapattr(__G)
  314.     __GDEF
  315. {
  316.     /* set archive bit (file is not backed up): */
  317.     G.pInfo->file_attr = (unsigned)(G.crec.external_file_attributes | 32) &
  318.       0xff;
  319.     return 0;
  320.  
  321. } /* end function mapattr() */
  322.  
  323.  
  324.  
  325.  
  326.  
  327. /************************/
  328. /*  Function mapname()  */
  329. /************************/
  330.  
  331. int mapname(__G__ renamed)   /* return 0 if no error, 1 if caution (filename */
  332.     __GDEF                   /* trunc), 2 if warning (skip file because dir */
  333.     int renamed;             /* doesn't exist), 3 if error (skip file), 10 */
  334. {                            /* if no memory (skip file) */
  335.     char pathcomp[FILNAMSIZ];    /* path-component buffer */
  336.     char *pp, *cp=(char *)NULL;  /* character pointers */
  337.     char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */
  338. #ifndef USE_VFAT
  339.     char *last_dot=(char *)NULL; /* last dot not converted to underscore */
  340. #endif
  341. #ifndef USE_VFAT
  342.     int dotname = FALSE;         /* flag:  path component begins with dot */
  343. #endif
  344.     int error = 0;
  345.     register unsigned workch;    /* hold the character being tested */
  346.  
  347.  
  348. /*---------------------------------------------------------------------------
  349.     Initialize various pointers and counters and stuff.
  350.   ---------------------------------------------------------------------------*/
  351.  
  352.     /* can create path as long as not just freshening, or if user told us */
  353.     G.create_dirs = (!G.fflag || renamed);
  354.  
  355.     created_dir = FALSE;        /* not yet */
  356.     renamed_fullpath = FALSE;
  357.  
  358.     if (renamed) {
  359.         cp = G.filename - 1;    /* point to beginning of renamed name... */
  360.         while (*++cp)
  361.             if (*cp == '\\')    /* convert backslashes to forward */
  362.                 *cp = '/';
  363.         cp = G.filename;
  364.         /* use temporary rootpath if user gave full pathname */
  365.         if (G.filename[0] == '/') {
  366.             renamed_fullpath = TRUE;
  367.             pathcomp[0] = '/';  /* copy the '/' and terminate */
  368.             pathcomp[1] = '\0';
  369.             ++cp;
  370.         } else if (isalpha(G.filename[0]) && G.filename[1] == ':') {
  371.             renamed_fullpath = TRUE;
  372.             pp = pathcomp;
  373.             *pp++ = *cp++;      /* copy the "d:" (+ '/', possibly) */
  374.             *pp++ = *cp++;
  375.             if (*cp == '/')
  376.                 *pp++ = *cp++;  /* otherwise add "./"? */
  377.             *pp = '\0';
  378.         }
  379.     }
  380.  
  381.     /* pathcomp is ignored unless renamed_fullpath is TRUE: */
  382.     if ((error = checkdir(__G__ pathcomp, INIT)) != 0)    /* initialize path buffer */
  383.         return error;           /* ...unless no mem or vol label on hard disk */
  384.  
  385.     *pathcomp = '\0';           /* initialize translation buffer */
  386.     pp = pathcomp;              /* point to translation buffer */
  387.     if (!renamed) {             /* cp already set if renamed */
  388.         if (G.jflag)            /* junking directories */
  389.             cp = (char *)strrchr(G.filename, '/');
  390.         if (cp == (char *)NULL) /* no '/' or not junking dirs */
  391.             cp = G.filename;    /* point to internal zipfile-member pathname */
  392.         else
  393.             ++cp;               /* point to start of last component of path */
  394.     }
  395.  
  396. /*---------------------------------------------------------------------------
  397.     Begin main loop through characters in filename.
  398.   ---------------------------------------------------------------------------*/
  399.  
  400.     while ((workch = (uch)*cp++) != 0) {
  401.  
  402.         switch (workch) {
  403.         case '/':             /* can assume -j flag not given */
  404.             *pp = '\0';
  405. #ifndef USE_VFAT
  406.             map2fat(pathcomp, last_dot);   /* 8.3 truncation (in place) */
  407.             last_dot = (char *)NULL;
  408. #endif
  409.             if ((error = checkdir(__G__ pathcomp, APPEND_DIR)) > 1)
  410.                 return error;
  411.             pp = pathcomp;    /* reset conversion buffer for next piece */
  412.             lastsemi = (char *)NULL; /* leave directory semi-colons alone */
  413.             break;
  414.  
  415.         /* drive names are not stored in zipfile, so no colons allowed;
  416.          *  no brackets or most other punctuation either (all of which
  417.          *  can appear in Unix-created archives; backslash is particularly
  418.          *  bad unless all necessary directories exist) */
  419.         case ':':
  420.         case '\\':
  421.         case '"':
  422. #ifndef USE_VFAT
  423.         case '[':
  424.         case ']':
  425.         case '+':
  426.         case ',':
  427.         case '=':
  428. #endif
  429.         case '<':
  430.         case '>':
  431.         case '|':
  432.         case '*':
  433.         case '?':
  434.             *pp++ = '_';
  435.             break;
  436.  
  437. #ifndef USE_VFAT
  438.         case '.':
  439.             if (pp == pathcomp) {     /* nothing appended yet... */
  440.                 if (*cp == '/') {     /* don't bother appending a "./" */
  441.                     ++cp;             /*  component to the path:  skip */
  442.                     break;            /*  to next char after the '/' */
  443.                 } else if (*cp == '.' && cp[1] == '/') {   /* "../" */
  444.                     *pp++ = '.';      /* add first dot, unchanged... */
  445.                     ++cp;             /* skip second dot, since it will */
  446.                 } else {              /*  be "added" at end of if-block */
  447.                     *pp++ = '_';      /* FAT doesn't allow null filename */
  448.                     dotname = TRUE;   /*  bodies, so map .exrc -> _.exrc */
  449.                 }                     /*  (extra '_' now, "dot" below) */
  450.             } else if (dotname) {     /* found a second dot, but still */
  451.                 dotname = FALSE;      /*  have extra leading underscore: */
  452.                 *pp = '\0';           /*  remove it by shifting chars */
  453.                 pp = pathcomp + 1;    /*  left one space (e.g., .p1.p2: */
  454.                 while (pp[1]) {       /*  __p1 -> _p1_p2 -> _p1.p2 when */
  455.                     *pp = pp[1];      /*  finished) [opt.:  since first */
  456.                     ++pp;             /*  two chars are same, can start */
  457.                 }                     /*  shifting at second position] */
  458.             }
  459.             last_dot = pp;    /* point at last dot so far... */
  460.             *pp++ = '_';      /* convert dot to underscore for now */
  461.             break;
  462. #endif
  463.  
  464.         case ';':             /* start of VMS version? */
  465. #ifdef USE_VFAT
  466.             lastsemi = pp;
  467.             *pp++ = ';';      /* keep for now; remove VMS ";##" */
  468. #else
  469.             lastsemi = pp;    /* omit for now; remove VMS vers. later */
  470. #endif
  471.             break;
  472.  
  473. #ifndef USE_VFAT
  474.         case ' ':             /* change spaces to underscore only */
  475.             if (G.sflag)      /*  if specifically requested */
  476.                 *pp++ = '_';
  477.             else
  478.                 *pp++ = (char)workch;
  479.             break;
  480. #endif
  481.  
  482.         default:
  483.             /* allow ASCII 255 and European characters in filenames: */
  484.             if (isprint(workch) || workch >= 127)
  485.                 *pp++ = (char)workch;
  486.         } /* end switch */
  487.     } /* end while loop */
  488.  
  489.     *pp = '\0';                   /* done with pathcomp:  terminate it */
  490.  
  491.     /* if not saving them, remove VMS version numbers (appended ";###") */
  492.     if (!G.V_flag && lastsemi) {
  493. #ifdef USE_VFAT
  494.         pp = lastsemi + 1;
  495. #else
  496.         pp = lastsemi;            /* semi-colon was omitted:  expect all #'s */
  497. #endif
  498.         while (isdigit((uch)(*pp)))
  499.             ++pp;
  500.         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
  501.             *lastsemi = '\0';
  502.     }
  503.  
  504. #ifndef USE_VFAT
  505.     map2fat(pathcomp, last_dot);  /* 8.3 truncation (in place) */
  506. #endif
  507.  
  508. /*---------------------------------------------------------------------------
  509.     Report if directory was created (and no file to create:  filename ended
  510.     in '/'), check name to be sure it exists, and combine path and name be-
  511.     fore exiting.
  512.   ---------------------------------------------------------------------------*/
  513.  
  514.     if (G.filename[strlen(G.filename) - 1] == '/') {
  515.         checkdir(__G__ G.filename, GETPATH);
  516.         if (created_dir && QCOND2) {
  517.             Info(slide, 0, ((char *)slide, LoadFarString(Creating),
  518.               G.filename));
  519.             return IZ_CREATED_DIR;   /* set dir time (note trailing '/') */
  520.         }
  521.         return 2;   /* dir existed already; don't look for data to extract */
  522.     }
  523.  
  524.     if (*pathcomp == '\0') {
  525.         Info(slide, 1, ((char *)slide, LoadFarString(ConversionFailed),
  526.           G.filename));
  527.         return 3;
  528.     }
  529.  
  530.     checkdir(__G__ pathcomp, APPEND_NAME);   /* returns 1 if truncated: care? */
  531.     checkdir(__G__ G.filename, GETPATH);
  532.  
  533.     if (G.pInfo->vollabel) {    /* set the volume label now */
  534.         if (QCOND2)
  535.             Info(slide, 0, ((char *)slide, LoadFarString(Labelling),
  536.               (nLabelDrive + 'a' - 1), G.filename));
  537.         if (volumelabel(G.filename)) {
  538.             Info(slide, 1, ((char *)slide, LoadFarString(ErrSetVolLabel)));
  539.             return 3;
  540.         }
  541.         return 2;   /* success:  skip the "extraction" quietly */
  542.     }
  543.  
  544.     return error;
  545.  
  546. } /* end function mapname() */
  547.  
  548.  
  549.  
  550.  
  551.  
  552. #ifndef USE_VFAT
  553. /**********************/
  554. /* Function map2fat() */
  555. /**********************/
  556.  
  557. static void map2fat(pathcomp, last_dot)
  558.     char *pathcomp, *last_dot;
  559. {
  560.     char *pEnd = pathcomp + strlen(pathcomp);
  561.  
  562. /*---------------------------------------------------------------------------
  563.     Case 1:  filename has no dot, so figure out if we should add one.  Note
  564.     that the algorithm does not try to get too fancy:  if there are no dots
  565.     already, the name either gets truncated at 8 characters or the last un-
  566.     derscore is converted to a dot (only if more characters are saved that
  567.     way).  In no case is a dot inserted between existing characters.
  568.  
  569.               GRR:  have problem if filename is volume label??
  570.  
  571.   ---------------------------------------------------------------------------*/
  572.  
  573.     /* pEnd = pathcomp + strlen(pathcomp); */
  574.     if (last_dot == (char *)NULL) {   /* no dots:  check for underscores... */
  575.         char *plu = strrchr(pathcomp, '_');   /* pointer to last underscore */
  576.  
  577.         if (plu == (char *)NULL) {  /* no dots, no underscores:  truncate at */
  578.             if (pEnd > pathcomp+8)  /* 8 chars (could insert '.' and keep 11) */
  579.                 *(pEnd = pathcomp+8) = '\0';
  580.         } else if (MIN(plu - pathcomp, 8) + MIN(pEnd - plu - 1, 3) > 8) {
  581.             last_dot = plu;       /* be lazy:  drop through to next if-block */
  582.         } else if ((pEnd - pathcomp) > 8)    /* more fits into just basename */
  583.             pathcomp[8] = '\0';    /* than if convert last underscore to dot */
  584.         /* else whole thing fits into 8 chars or less:  no change */
  585.     }
  586.  
  587. /*---------------------------------------------------------------------------
  588.     Case 2:  filename has dot in it, so truncate first half at 8 chars (shift
  589.     extension if necessary) and second half at three.
  590.   ---------------------------------------------------------------------------*/
  591.  
  592.     if (last_dot != (char *)NULL) {   /* one dot (or two, in the case of */
  593.         *last_dot = '.';              /*  "..") is OK:  put it back in */
  594.  
  595.         if ((last_dot - pathcomp) > 8) {
  596.             char *p=last_dot, *q=pathcomp+8;
  597.             int i;
  598.  
  599.             for (i = 0;  (i < 4) && *p;  ++i)  /* too many chars in basename: */
  600.                 *q++ = *p++;                   /*  shift extension left and */
  601.             *q = '\0';                         /*  truncate/terminate it */
  602.         } else if ((pEnd - last_dot) > 4)
  603.             last_dot[4] = '\0';                /* too many chars in extension */
  604.         /* else filename is fine as is:  no change */
  605.     }
  606. } /* end function map2fat() */
  607. #endif
  608.  
  609.  
  610.  
  611.  
  612.  
  613. /***********************/
  614. /* Function checkdir() */
  615. /***********************/
  616.  
  617. int checkdir(__G__ pathcomp, flag)
  618.     __GDEF
  619.     char *pathcomp;
  620.     int flag;
  621. /*
  622.  * returns:  1 - (on APPEND_NAME) truncated filename
  623.  *           2 - path doesn't exist, not allowed to create
  624.  *           3 - path doesn't exist, tried to create and failed; or
  625.  *               path exists and is not a directory, but is supposed to be
  626.  *           4 - path is too long
  627.  *          10 - can't allocate memory for filename buffers
  628.  */
  629. {
  630.     static int rootlen = 0;   /* length of rootpath */
  631.     static char *rootpath;    /* user's "extract-to" directory */
  632.     static char *buildpath;   /* full path (so far) to extracted file */
  633.     static char *end;         /* pointer to end of buildpath ('\0') */
  634. #ifdef MSC
  635.     int attrs;                /* work around MSC stat() bug */
  636. #endif
  637.  
  638. #   define FN_MASK   7
  639. #   define FUNCTION  (flag & FN_MASK)
  640.  
  641.  
  642.  
  643. /*---------------------------------------------------------------------------
  644.     APPEND_DIR:  append the path component to the path being built and check
  645.     for its existence.  If doesn't exist and we are creating directories, do
  646.     so for this one; else signal success or error as appropriate.
  647.   ---------------------------------------------------------------------------*/
  648.  
  649.     if (FUNCTION == APPEND_DIR) {
  650.         int too_long = FALSE;
  651.  
  652.         Trace((stderr, "appending dir segment [%s]\n", pathcomp));
  653.         while ((*end = *pathcomp++) != '\0')
  654.             ++end;
  655.  
  656.         /* GRR:  could do better check, see if overrunning buffer as we go:
  657.          * check end-buildpath after each append, set warning variable if
  658.          * within 20 of FILNAMSIZ; then if var set, do careful check when
  659.          * appending.  Clear variable when begin new path. */
  660.  
  661.         if ((end-buildpath) > FILNAMSIZ-3)  /* need '/', one-char name, '\0' */
  662.             too_long = TRUE;                /* check if extracting directory? */
  663. #ifdef MSC /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
  664.         if (_dos_getfileattr(buildpath, &attrs) || stat(buildpath, &G.statbuf))
  665. #else
  666.         if (stat(buildpath, &G.statbuf))    /* path doesn't exist */
  667. #endif
  668.         {
  669.             if (!G.create_dirs) { /* told not to create (freshening) */
  670.                 free(buildpath);
  671.                 return 2;         /* path doesn't exist:  nothing to do */
  672.             }
  673.             if (too_long) {
  674.                 Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
  675.                   buildpath));
  676.                 free(buildpath);
  677.                 return 4;         /* no room for filenames:  fatal */
  678.             }
  679.             if (MKDIR(buildpath, 0777) == -1) {   /* create the directory */
  680.                 Info(slide, 1, ((char *)slide, LoadFarString(CantCreateDir),
  681.                   buildpath, G.filename));
  682.                 free(buildpath);
  683.                 return 3;      /* path didn't exist, tried to create, failed */
  684.             }
  685.             created_dir = TRUE;
  686.         } else if (!S_ISDIR(G.statbuf.st_mode)) {
  687.             Info(slide, 1, ((char *)slide, LoadFarString(DirIsntDirectory),
  688.               buildpath, G.filename));
  689.             free(buildpath);
  690.             return 3;          /* path existed but wasn't dir */
  691.         }
  692.         if (too_long) {
  693.             Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong),
  694.               buildpath));
  695.             free(buildpath);
  696.             return 4;         /* no room for filenames:  fatal */
  697.         }
  698.         *end++ = '/';
  699.         *end = '\0';
  700.         Trace((stderr, "buildpath now = [%s]\n", buildpath));
  701.         return 0;
  702.  
  703.     } /* end if (FUNCTION == APPEND_DIR) */
  704.  
  705. /*---------------------------------------------------------------------------
  706.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  707.     buildpath.
  708.   ---------------------------------------------------------------------------*/
  709.  
  710.     if (FUNCTION == GETPATH) {
  711.         strcpy(pathcomp, buildpath);
  712.         Trace((stderr, "getting and freeing path [%s]\n", pathcomp));
  713.         free(buildpath);
  714.         buildpath = end = (char *)NULL;
  715.         return 0;
  716.     }
  717.  
  718. /*---------------------------------------------------------------------------
  719.     APPEND_NAME:  assume the path component is the filename; append it and
  720.     return without checking for existence.
  721.   ---------------------------------------------------------------------------*/
  722.  
  723.     if (FUNCTION == APPEND_NAME) {
  724. #ifdef NOVELL_BUG_WORKAROUND
  725.         if (end == buildpath) {
  726.             /* work-around for Novell's "overwriting executables" bug:
  727.                prepend "./" to name when no path component is specified */
  728.             *end++ = '.';
  729.             *end++ = '/';
  730.         }
  731. #endif /* NOVELL_BUG_WORKAROUND */
  732.         Trace((stderr, "appending filename [%s]\n", pathcomp));
  733.         while ((*end = *pathcomp++) != '\0') {
  734.             ++end;
  735.             if ((end-buildpath) >= FILNAMSIZ) {
  736.                 *--end = '\0';
  737.                 Info(slide, 1, ((char *)slide, LoadFarString(PathTooLongTrunc),
  738.                   G.filename, buildpath));
  739.                 return 1;   /* filename truncated */
  740.             }
  741.         }
  742.         Trace((stderr, "buildpath now = [%s]\n", buildpath));
  743.         return 0;  /* could check for existence here, prompt for new name... */
  744.     }
  745.  
  746. /*---------------------------------------------------------------------------
  747.     INIT:  allocate and initialize buffer space for the file currently being
  748.     extracted.  If file was renamed with an absolute path, don't prepend the
  749.     extract-to path.
  750.   ---------------------------------------------------------------------------*/
  751.  
  752.     if (FUNCTION == INIT) {
  753.         Trace((stderr, "initializing buildpath to "));
  754.         /* allocate space for full filename, root path, and maybe "./" */
  755.         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) ==
  756.             (char *)NULL)
  757.             return 10;
  758.         if (G.pInfo->vollabel) {
  759. /* GRR:  for network drives, do strchr() and return IZ_VOL_LABEL if not [1] */
  760.             if (renamed_fullpath && pathcomp[1] == ':')
  761.                 *buildpath = ToLower(*pathcomp);
  762.             else if (!renamed_fullpath && rootlen > 1 && rootpath[1] == ':')
  763.                 *buildpath = ToLower(*rootpath);
  764.             else {
  765.                 GETDRIVE(nLabelDrive);   /* assumed that a == 1, b == 2, etc. */
  766.                 *buildpath = (char)(nLabelDrive - 1 + 'a');
  767.             }
  768.             nLabelDrive = *buildpath - 'a' + 1;        /* save for mapname() */
  769.             if (G.volflag == 0 || *buildpath < 'a' ||  /* no label/bogus disk */
  770.                (G.volflag == 1 && !isfloppy(nLabelDrive)))  /* -$:  no fixed */
  771.             {
  772.                 free(buildpath);
  773.                 return IZ_VOL_LABEL;     /* skipping with message */
  774.             }
  775.             *buildpath = '\0';
  776.             end = buildpath;
  777.         } else if (renamed_fullpath) {   /* pathcomp = valid data */
  778.             end = buildpath;
  779.             while ((*end = *pathcomp++) != '\0')
  780.                 ++end;
  781.         } else if (rootlen > 0) {
  782.             strcpy(buildpath, rootpath);
  783.             end = buildpath + rootlen;
  784.         } else {
  785.             *buildpath = '\0';
  786.             end = buildpath;
  787.         }
  788.         Trace((stderr, "[%s]\n", buildpath));
  789.         return 0;
  790.     }
  791.  
  792. /*---------------------------------------------------------------------------
  793.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  794.     sary; else assume it's a zipfile member and return.  This path segment
  795.     gets used in extracting all members from every zipfile specified on the
  796.     command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
  797.     directory specification includes a drive letter (leading "x:"), it is
  798.     treated just as if it had a trailing '/'--that is, one directory level
  799.     will be created if the path doesn't exist, unless this is otherwise pro-
  800.     hibited (e.g., freshening).
  801.   ---------------------------------------------------------------------------*/
  802.  
  803. #if (!defined(SFX) || defined(SFX_EXDIR))
  804.     if (FUNCTION == ROOT) {
  805.         Trace((stderr, "initializing root path to [%s]\n", pathcomp));
  806.         if (pathcomp == (char *)NULL) {
  807.             rootlen = 0;
  808.             return 0;
  809.         }
  810.         if ((rootlen = strlen(pathcomp)) > 0) {
  811.             int had_trailing_pathsep=FALSE, has_drive=FALSE, xtra=2;
  812.  
  813.             if (isalpha(pathcomp[0]) && pathcomp[1] == ':')
  814.                 has_drive = TRUE;   /* drive designator */
  815.             if (pathcomp[rootlen-1] == '/') {
  816.                 pathcomp[--rootlen] = '\0';
  817.                 had_trailing_pathsep = TRUE;
  818.             }
  819.             if (has_drive && (rootlen == 2)) {
  820.                 if (!had_trailing_pathsep)   /* i.e., original wasn't "x:/" */
  821.                     xtra = 3;      /* room for '.' + '/' + 0 at end of "x:" */
  822.             } else if (rootlen > 0) {     /* need not check "x:." and "x:/" */
  823. #ifdef MSC
  824.                 /* MSC 6.00 bug:  stat(non-existent-dir) == 0 [exists!] */
  825.                 if (_dos_getfileattr(pathcomp, &attrs) ||
  826.                     SSTAT(pathcomp,&G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
  827. #else
  828.                 if (SSTAT(pathcomp,&G.statbuf) || !S_ISDIR(G.statbuf.st_mode))
  829. #endif
  830.                 {
  831.                     /* path does not exist */
  832.                     if (!G.create_dirs /* || iswild(pathcomp) */ ) {
  833.                         rootlen = 0;
  834.                         return 2;   /* treat as stored file */
  835.                     }
  836. /* GRR:  scan for wildcard characters?  OS-dependent...  if find any, return 2:
  837.  * treat as stored file(s) */
  838.                     /* create directory (could add loop here to scan pathcomp
  839.                      * and create more than one level, but really necessary?) */
  840.                     if (MKDIR(pathcomp, 0777) == -1) {
  841.                         Info(slide, 1, ((char *)slide,
  842.                           LoadFarString(CantCreateExtractDir), pathcomp));
  843.                         rootlen = 0;   /* path didn't exist, tried to create, */
  844.                         return 3;  /* failed:  file exists, or need 2+ levels */
  845.                     }
  846.                 }
  847.             }
  848.             if ((rootpath = (char *)malloc(rootlen+xtra)) == (char *)NULL) {
  849.                 rootlen = 0;
  850.                 return 10;
  851.             }
  852.             strcpy(rootpath, pathcomp);
  853.             if (xtra == 3)                  /* had just "x:", make "x:." */
  854.                 rootpath[rootlen++] = '.';
  855.             rootpath[rootlen++] = '/';
  856.             rootpath[rootlen] = '\0';
  857.             Trace((stderr, "rootpath now = [%s]\n", rootpath));
  858.         }
  859.         return 0;
  860.     }
  861. #endif /* !SFX || SFX_EXDIR */
  862.  
  863. /*---------------------------------------------------------------------------
  864.     END:  free rootpath, immediately prior to program exit.
  865.   ---------------------------------------------------------------------------*/
  866.  
  867.     if (FUNCTION == END) {
  868.         Trace((stderr, "freeing rootpath\n"));
  869.         if (rootlen > 0)
  870.             free(rootpath);
  871.         return 0;
  872.     }
  873.  
  874.     return 99;  /* should never reach */
  875.  
  876. } /* end function checkdir() */
  877.  
  878.  
  879.  
  880.  
  881.  
  882.  
  883. /***********************/
  884. /* Function isfloppy() */
  885. /***********************/
  886.  
  887. static int isfloppy(nDrive)  /* more precisely, is it removable? */
  888.     int nDrive;
  889. {
  890.     union REGS regs;
  891.  
  892.     regs.h.ah = 0x44;
  893.     regs.h.al = 0x08;
  894.     regs.h.bl = (uch)nDrive;
  895. #ifdef __EMX__
  896.     _int86(0x21, ®s, ®s);
  897.     if (WREGS(regs,flags) & 1)
  898. #else
  899.     intdos(®s, ®s);
  900.     if (WREGS(regs,cflag))        /* error:  do default a/b check instead */
  901. #endif
  902.     {
  903.         Trace((stderr,
  904.           "error in DOS function 0x44 (AX = 0x%04x):  guessing instead...\n",
  905.           WREGS(regs,ax)));
  906.         return (nDrive == 1 || nDrive == 2)? TRUE : FALSE;
  907.     } else
  908.         return WREGS(regs,ax)? FALSE : TRUE;
  909. }
  910.  
  911.  
  912.  
  913.  
  914. #if (!defined(__GO32__) && !defined(__EMX__))
  915.  
  916. typedef struct dosfcb {
  917.     uch  flag;        /* ff to indicate extended FCB */
  918.     char res[5];      /* reserved */
  919.     uch  vattr;       /* attribute */
  920.     uch  drive;       /* drive (1=A, 2=B, ...) */
  921.     uch  vn[11];      /* file or volume name */
  922.     char dmmy[5];
  923.     uch  nn[11];      /* holds new name if renaming (else reserved) */
  924.     char dmmy2[9];
  925. } dos_fcb;
  926.  
  927. /**************************/
  928. /* Function volumelabel() */
  929. /**************************/
  930.  
  931. static int volumelabel(newlabel)
  932.     char *newlabel;
  933. {
  934. #ifdef DEBUG
  935.     char *p;
  936. #endif
  937.     int len = strlen(newlabel);
  938.     dos_fcb  fcb, dta, far *pfcb=&fcb, far *pdta=&dta;
  939.     struct SREGS sregs;
  940.     union REGS regs;
  941.  
  942.  
  943. /*---------------------------------------------------------------------------
  944.     Label the diskette specified by nLabelDrive using FCB calls.  (Old ver-
  945.     sions of MS-DOS and OS/2 DOS boxes can't use DOS function 3Ch to create
  946.     labels.)  Must use far pointers for MSC FP_* macros to work; must pad
  947.     FCB filenames with spaces; and cannot include dot in 8th position.  May
  948.     or may not need to zero out FCBs before using; do so just in case.
  949.   ---------------------------------------------------------------------------*/
  950.  
  951.     memset((char *)&dta, 0, sizeof(dos_fcb));
  952.     memset((char *)&fcb, 0, sizeof(dos_fcb));
  953.  
  954. #ifdef DEBUG
  955.     for (p = (char *)&dta; (p - (char *)&dta) < sizeof(dos_fcb); ++p)
  956.         if (*p)
  957.             fprintf(stderr, "error:  dta[%d] = %x\n", (p - (char *)&dta), *p);
  958.     for (p = (char *)&fcb; (p - (char *)&fcb) < sizeof(dos_fcb); ++p)
  959.         if (*p)
  960.             fprintf(stderr, "error:  fcb[%d] = %x\n", (p - (char *)&fcb), *p);
  961.     printf("testing pointer macros:\n");
  962.     segread(&sregs);
  963.     printf("cs = %x, ds = %x, es = %x, ss = %x\n", sregs.cs, sregs.ds, sregs.es,
  964.       sregs.ss);
  965. #endif /* DEBUG */
  966.  
  967. #if 0
  968. #ifdef __TURBOC__
  969.     bdosptr(0x1a, dta, DO_NOT_CARE);
  970. #else
  971.     (intdosx method below)
  972. #endif
  973. #endif /* 0 */
  974.  
  975.     /* set the disk transfer address for subsequent FCB calls */
  976.     sregs.ds = FP_SEG(pdta);
  977.     WREGS(regs,dx) = FP_OFF(pdta);
  978.     Trace((stderr, "segment:offset of pdta = %x:%x\n", sregs.ds, WREGS(regs,dx)));
  979.     Trace((stderr, "&dta = %lx, pdta = %lx\n", (ulg)&dta, (ulg)pdta));
  980.     regs.h.ah = 0x1a;
  981.     intdosx(®s, ®s, &sregs);
  982.  
  983.     /* fill in the FCB */
  984.     sregs.ds = FP_SEG(pfcb);
  985.     WREGS(regs,dx) = FP_OFF(pfcb);
  986.     pfcb->flag = 0xff;          /* extended FCB */
  987.     pfcb->vattr = 0x08;         /* attribute:  disk volume label */
  988.     pfcb->drive = (uch)nLabelDrive;
  989.  
  990. #ifdef DEBUG
  991.     Trace((stderr, "segment:offset of pfcb = %x:%x\n", sregs.ds, WREGS(regs,dx)));
  992.     Trace((stderr, "&fcb = %lx, pfcb = %lx\n", (ulg)&fcb, (ulg)pfcb));
  993.     Trace((stderr, "(2nd check:  labelling drive %c:)\n", pfcb->drive-1+'A'));
  994.     if (pfcb->flag != fcb.flag)
  995.         fprintf(stderr, "error:  pfcb->flag = %d, fcb.flag = %d\n",
  996.           pfcb->flag, fcb.flag);
  997.     if (pfcb->drive != fcb.drive)
  998.         fprintf(stderr, "error:  pfcb->drive = %d, fcb.drive = %d\n",
  999.           pfcb->drive, fcb.drive);
  1000.     if (pfcb->vattr != fcb.vattr)
  1001.         fprintf(stderr, "error:  pfcb->vattr = %d, fcb.vattr = %d\n",
  1002.           pfcb->vattr, fcb.vattr);
  1003. #endif /* DEBUG */
  1004.  
  1005.     /* check for existing label */
  1006.     Trace((stderr, "searching for existing label via FCBs\n"));
  1007.     regs.h.ah = 0x11;      /* FCB find first */
  1008. #if 0  /* THIS STRNCPY FAILS (MSC bug?): */
  1009.     strncpy(pfcb->vn, "???????????", 11);   /* i.e., "*.*" */
  1010.     Trace((stderr, "pfcb->vn = %lx\n", (ulg)pfcb->vn));
  1011.     Trace((stderr, "flag = %x, drive = %d, vattr = %x, vn = %s = %s.\n",
  1012.       fcb.flag, fcb.drive, fcb.vattr, fcb.vn, pfcb->vn));
  1013. #endif
  1014.     strncpy((char *)fcb.vn, "???????????", 11);   /* i.e., "*.*" */
  1015.     Trace((stderr, "fcb.vn = %lx\n", (ulg)fcb.vn));
  1016.     Trace((stderr, "regs.h.ah = %x, regs.x.dx = %04x, sregs.ds = %04x\n",
  1017.       regs.h.ah, WREGS(regs,dx), sregs.ds));
  1018.     Trace((stderr, "flag = %x, drive = %d, vattr = %x, vn = %s = %s.\n",
  1019.       fcb.flag, fcb.drive, fcb.vattr, fcb.vn, pfcb->vn));
  1020.     intdosx(®s, ®s, &sregs);
  1021.  
  1022. /*---------------------------------------------------------------------------
  1023.     If not previously labelled, write a new label.  Otherwise just rename,
  1024.     since MS-DOS 2.x has a bug which damages the FAT when the old label is
  1025.     deleted.
  1026.   ---------------------------------------------------------------------------*/
  1027.  
  1028.     if (regs.h.al) {
  1029.         Trace((stderr, "no label found\n\n"));
  1030.         regs.h.ah = 0x16;                 /* FCB create file */
  1031.         strncpy((char *)fcb.vn, newlabel, len);
  1032.         if (len < 11)   /* fill with spaces */
  1033.             strncpy((char *)(fcb.vn+len), "           ", 11-len);
  1034.         Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lx\n", (ulg)fcb.vn,
  1035.           (ulg)pfcb->vn));
  1036.         Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
  1037.           fcb.drive, fcb.vattr));
  1038.         Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
  1039.         intdosx(®s, ®s, &sregs);
  1040.         regs.h.ah = 0x10;                 /* FCB close file */
  1041.         if (regs.h.al) {
  1042.             Trace((stderr, "unable to write volume name (AL = %x)\n",
  1043.               regs.h.al));
  1044.             intdosx(®s, ®s, &sregs);
  1045.             return 1;
  1046.         } else {
  1047.             intdosx(®s, ®s, &sregs);
  1048.             Trace((stderr, "new volume label [%s] written\n", newlabel));
  1049.             return 0;
  1050.         }
  1051.     } else {
  1052.         Trace((stderr, "found old label [%s]\n\n", dta.vn));  /* not term. */
  1053.         regs.h.ah = 0x17;                 /* FCB rename */
  1054.         strncpy((char *)fcb.vn, (char *)dta.vn, 11);
  1055.         strncpy((char *)fcb.nn, newlabel, len);
  1056.         if (len < 11)                     /* fill with spaces */
  1057.             strncpy((char *)(fcb.nn+len), "           ", 11-len);
  1058.         Trace((stderr, "fcb.vn = %lx  pfcb->vn = %lx\n", (ulg)fcb.vn,
  1059.           (ulg)pfcb->vn));
  1060.         Trace((stderr, "fcb.nn = %lx  pfcb->nn = %lx\n", (ulg)fcb.nn,
  1061.           (ulg)pfcb->nn));
  1062.         Trace((stderr, "flag = %x, drive = %d, vattr = %x\n", fcb.flag,
  1063.           fcb.drive, fcb.vattr));
  1064.         Trace((stderr, "vn = %s = %s.\n", fcb.vn, pfcb->vn));
  1065.         Trace((stderr, "nn = %s = %s.\n", fcb.nn, pfcb->nn));
  1066.         intdosx(®s, ®s, &sregs);
  1067.         if (regs.h.al) {
  1068.             Trace((stderr, "Unable to change volume name (AL = %x)\n",
  1069.               regs.h.al));
  1070.             return 1;
  1071.         } else {
  1072.             Trace((stderr, "volume label changed to [%s]\n", newlabel));
  1073.             return 0;
  1074.         }
  1075.     }
  1076. } /* end function volumelabel() */
  1077.  
  1078. #endif /* !__GO32__ && !__EMX__ */
  1079.  
  1080.  
  1081.  
  1082.  
  1083.  
  1084. /****************************/
  1085. /* Function close_outfile() */
  1086. /****************************/
  1087.  
  1088. void close_outfile(__G)
  1089.     __GDEF
  1090.  /*
  1091.   * MS-DOS VERSION
  1092.   *
  1093.   * Set the output file date/time stamp according to information from the
  1094.   * zipfile directory record for this member, then close the file and set
  1095.   * its permissions (archive, hidden, read-only, system).  Aside from closing
  1096.   * the file, this routine is optional (but most compilers support it).
  1097.   */
  1098. {
  1099. #ifdef USE_EF_UX_TIME
  1100.     ztimbuf z_utime;
  1101.  
  1102.     /* The following DOS date/time structure is machine dependent as it
  1103.      * assumes "little endian" byte order. For MSDOS specific code, which
  1104.      * is run on x86 CPUs (or emulators), this assumption is valid; but
  1105.      * care should be taken when using this code as template for other ports.
  1106.      */
  1107.     union {
  1108.         ulg z_dostime;
  1109. # ifdef __TURBOC__
  1110.         struct ftime ft;        /* system file time record */
  1111. # endif
  1112.         struct {                /* date and time words */
  1113.             union {             /* DOS file modification time word */
  1114.                 ush ztime;
  1115.                 struct {
  1116.                     unsigned zt_se : 5;
  1117.                     unsigned zt_mi : 6;
  1118.                     unsigned zt_hr : 5;
  1119.                 } _tf;
  1120.             } _t;
  1121.             union {             /* DOS file modification date word */
  1122.                 ush zdate;
  1123.                 struct {
  1124.                     unsigned zd_dy : 5;
  1125.                     unsigned zd_mo : 4;
  1126.                     unsigned zd_yr : 7;
  1127.                 } _df;
  1128.             } _d;
  1129.         } zt;
  1130.     } dos_dt;
  1131. #else /* !USE_EF_UX_TIME */
  1132. # ifdef __TURBOC__
  1133.     union {
  1134.         struct ftime ft;        /* system file time record */
  1135.         struct {
  1136.             ush ztime;          /* date and time words */
  1137.             ush zdate;          /* .. same format as in .ZIP file */
  1138.         } zt;
  1139.     } dos_dt;
  1140. # endif
  1141. #endif /* ?USE_EF_UX_TIME */
  1142.  
  1143.  
  1144. /*---------------------------------------------------------------------------
  1145.     Copy and/or convert time and date variables, if necessary; then set the
  1146.     file time/date.  WEIRD BORLAND "BUG":  if output is buffered, and if run
  1147.     under at least some versions of DOS (e.g., 6.0), and if files are smaller
  1148.     than DOS physical block size (i.e., 512 bytes) (?), then files MAY NOT
  1149.     get timestamped correctly--apparently setftime() occurs before any data
  1150.     are written to the file, and when file is closed and buffers are flushed,
  1151.     timestamp is overwritten with current time.  Even with a 32K buffer, this
  1152.     does not seem to occur with larger files.  UnZip output is now unbuffered,
  1153.     but if it were not, could still avoid problem by adding "fflush(outfile)"
  1154.     just before setftime() call.  Weird, huh?
  1155.   ---------------------------------------------------------------------------*/
  1156.  
  1157. #ifdef USE_EF_UX_TIME
  1158.     if (G.extra_field &&
  1159.         ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length,
  1160.                          &z_utime, NULL) > 0) {
  1161.         struct tm *t;
  1162.  
  1163.         TTrace((stderr, "close_outfile:  Unix e.f. modif. time = %ld\n",
  1164.           z_utime.modtime));
  1165.         /* round up to even seconds */
  1166.         z_utime.modtime = (z_utime.modtime + 1) & (~1);
  1167.         t = localtime(&(z_utime.modtime));
  1168.         if (t->tm_year < 80) {
  1169.             dos_dt.zt._t._tf.zt_se = 0;
  1170.             dos_dt.zt._t._tf.zt_mi = 0;
  1171.             dos_dt.zt._t._tf.zt_hr = 0;
  1172.             dos_dt.zt._d._df.zd_dy = 1;
  1173.             dos_dt.zt._d._df.zd_mo = 1;
  1174.             dos_dt.zt._d._df.zd_yr = 0;
  1175.         } else {
  1176.             dos_dt.zt._t._tf.zt_se = t->tm_sec >> 1;
  1177.             dos_dt.zt._t._tf.zt_mi = t->tm_min;
  1178.             dos_dt.zt._t._tf.zt_hr = t->tm_hour;
  1179.             dos_dt.zt._d._df.zd_dy = t->tm_mday;
  1180.             dos_dt.zt._d._df.zd_mo = t->tm_mon + 1;
  1181.             dos_dt.zt._d._df.zd_yr = t->tm_year - 80;
  1182.         }
  1183.     } else {
  1184.         dos_dt.zt._t.ztime = G.lrec.last_mod_file_time;
  1185.         dos_dt.zt._d.zdate = G.lrec.last_mod_file_date;
  1186.     }
  1187. # ifdef __TURBOC__
  1188.     setftime(fileno(G.outfile), &dos_dt.ft);
  1189. # else
  1190.     _dos_setftime(fileno(G.outfile), dos_dt.zt._d.zdate, dos_dt.zt._t.ztime);
  1191. # endif
  1192. #else /* !USE_EF_UX_TIME */
  1193. # ifdef __TURBOC__
  1194.     dos_dt.zt.ztime = G.lrec.last_mod_file_time;
  1195.     dos_dt.zt.zdate = G.lrec.last_mod_file_date;
  1196.     setftime(fileno(G.outfile), &dos_dt.ft);
  1197. # else
  1198.     _dos_setftime(fileno(G.outfile), G.lrec.last_mod_file_date,
  1199.                                      G.lrec.last_mod_file_time);
  1200. # endif
  1201. #endif /* ?USE_EF_UX_TIME */
  1202.  
  1203. /*---------------------------------------------------------------------------
  1204.     And finally we can close the file...at least everybody agrees on how to
  1205.     do *this*.  I think...  Also change the mode according to the stored file
  1206.     attributes, since we didn't do that when we opened the dude.
  1207.   ---------------------------------------------------------------------------*/
  1208.  
  1209.     fclose(G.outfile);
  1210.  
  1211. #ifdef __TURBOC__
  1212. #   if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0452))
  1213. #     define Chmod  _rtl_chmod
  1214. #   else
  1215. #     define Chmod  _chmod
  1216. #   endif
  1217.     if (Chmod(G.filename, 1, G.pInfo->file_attr) != G.pInfo->file_attr)
  1218.         Info(slide, 1, ((char *)slide, LoadFarString(AttribsMayBeWrong)));
  1219. #else /* !__TURBOC__ */
  1220.     _dos_setfileattr(G.filename, G.pInfo->file_attr);
  1221. #endif /* ?__TURBOC__ */
  1222.  
  1223. } /* end function close_outfile() */
  1224.  
  1225.  
  1226.  
  1227.  
  1228.  
  1229. #ifndef SFX
  1230.  
  1231. /*************************/
  1232. /* Function dateformat() */
  1233. /*************************/
  1234.  
  1235. int dateformat()
  1236. {
  1237.  
  1238. /*---------------------------------------------------------------------------
  1239.     For those operating systems which support it, this function returns a
  1240.     value which tells how national convention says that numeric dates are
  1241.     displayed.  Return values are DF_YMD, DF_DMY and DF_MDY (the meanings
  1242.     should be fairly obvious).
  1243.   ---------------------------------------------------------------------------*/
  1244.  
  1245. #ifndef MSWIN
  1246.     unsigned short CountryInfo[18];
  1247. #if (!defined(__GO32__) && !defined(__EMX__))
  1248.     unsigned short far *_CountryInfo = CountryInfo;
  1249.     struct SREGS sregs;
  1250.     union REGS regs;
  1251.  
  1252. #ifdef __WATCOMC__
  1253.     segread(&sregs);
  1254. #endif
  1255.     sregs.ds  = FP_SEG(_CountryInfo);
  1256.     WREGS(regs,dx) = FP_OFF(_CountryInfo);
  1257.     WREGS(regs,ax) = 0x3800;
  1258.     int86x(0x21, ®s, ®s, &sregs);
  1259.  
  1260. #else /* __GO32__ || __EMX__ */
  1261.  
  1262.     _dos_getcountryinfo(CountryInfo);
  1263. #endif
  1264.  
  1265.     switch(CountryInfo[0]) {
  1266.         case 0:
  1267.             return DF_MDY;
  1268.         case 1:
  1269.             return DF_DMY;
  1270.         case 2:
  1271.             return DF_YMD;
  1272.     }
  1273. #endif /* !MSWIN */
  1274.  
  1275.     return DF_MDY;   /* default for systems without locale info */
  1276.  
  1277. } /* end function dateformat() */
  1278.  
  1279.  
  1280.  
  1281.  
  1282.  
  1283. /************************/
  1284. /*  Function version()  */
  1285. /************************/
  1286.  
  1287. void version(__G)
  1288.     __GDEF
  1289. {
  1290.     int len;
  1291. #if defined(__DJGPP__) || defined(__WATCOMC__) || \
  1292.     (defined(_MSC_VER) && (_MSC_VER != 800))
  1293.     char buf[80];
  1294. #endif
  1295.  
  1296.     len = sprintf((char *)slide, LoadFarString(CompiledWith),
  1297.  
  1298. #ifdef __GNUC__
  1299. #  ifdef __DJGPP__
  1300.       (sprintf(buf, "djgpp v%d / gcc ", __DJGPP__), buf),
  1301. #  elif __GO32__                  /* __GO32__ is defined as "1" only (sigh) */
  1302.       "djgpp v1.x / gcc ",
  1303. #  elif defined(__EMX__)          /* ...so is __EMX__ (double sigh) */
  1304.       "emx+gcc ",
  1305. #  else
  1306.       "gcc ",
  1307. #  endif
  1308.       __VERSION__,
  1309. #elif defined(__WATCOMC__)
  1310. #  if (__WATCOMC__ % 10 != 0)
  1311.       "Watcom C/C++", (sprintf(buf, " %d.%02d", __WATCOMC__ / 100,
  1312.                                __WATCOMC__ % 100), buf),
  1313. #  else
  1314.       "Watcom C/C++", (sprintf(buf, " %d.%d", __WATCOMC__ / 100,
  1315.                                (__WATCOMC__ % 100) / 10), buf),
  1316. #  endif
  1317. #elif defined(__TURBOC__)
  1318. #  ifdef __BORLANDC__
  1319.       "Borland C++",
  1320. #    if (__BORLANDC__ < 0x0200)
  1321.         " 1.0",
  1322. #    elif (__BORLANDC__ == 0x0200)   /* James:  __TURBOC__ = 0x0297 */
  1323.         " 2.0",
  1324. #    elif (__BORLANDC__ == 0x0400)
  1325.         " 3.0",
  1326. #    elif (__BORLANDC__ == 0x0410)   /* __BCPLUSPLUS__ = 0x0310 */
  1327.         " 3.1",
  1328. #    elif (__BORLANDC__ == 0x0452)   /* __BCPLUSPLUS__ = 0x0320 */
  1329.         " 4.0 or 4.02",
  1330. #    elif (__BORLANDC__ == 0x0460)   /* __BCPLUSPLUS__ = 0x0340 */
  1331.         " 4.5",
  1332. #    elif (__BORLANDC__ == 0x0500)
  1333.         " 5.0",
  1334. #    else
  1335.         " later than 5.0",
  1336. #    endif
  1337. #  else
  1338.       "Turbo C",
  1339. #    if (__TURBOC__ > 0x0500)
  1340.         "++ later than 5.0",
  1341. #    elif (__TURBOC__ == 0x0500)     /* Mike W:  5.0 -> 0x0500 */
  1342.         "++ 5.0",
  1343. #    elif (__TURBOC__ >= 0x0400)     /* Kevin:  3.0 -> 0x0401 */
  1344.         "++ 3.0 or 4.x",
  1345. #    elif (__TURBOC__ == 0x0295)     /* [661] vfy'd by Kevin */
  1346.         "++ 1.0",
  1347. #    elif ((__TURBOC__ >= 0x018d) && (__TURBOC__ <= 0x0200)) /* James: 0x0200 */
  1348.         " 2.0",
  1349. #    elif (__TURBOC__ > 0x0100)
  1350.         " 1.5",                      /* James:  0x0105? */
  1351. #    else
  1352.         " 1.0",                      /* James:  0x0100 */
  1353. #    endif
  1354. #  endif
  1355. #elif defined(MSC)
  1356.       "Microsoft C ",
  1357. #  ifdef _MSC_VER
  1358. #    if (_MSC_VER == 800)
  1359.         "8.0/8.0c (Visual C++ 1.0/1.5)",
  1360. #    else
  1361.         (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf),
  1362. #    endif
  1363. #  else
  1364.       "5.1 or earlier",
  1365. #  endif
  1366. #else
  1367.       "unknown compiler", "",
  1368. #endif /* ?compilers */
  1369.  
  1370.       "MS-DOS",
  1371.  
  1372. #if (defined(__GNUC__) || defined(WATCOMC_386))
  1373.       " (32-bit)",
  1374. #else
  1375. #  if defined(M_I86HM) || defined(__HUGE__)
  1376.       " (16-bit, huge)",
  1377. #  else
  1378. #  if defined(M_I86LM) || defined(__LARGE__)
  1379.       " (16-bit, large)",
  1380. #  else
  1381. #  if defined(M_I86MM) || defined(__MEDIUM__)
  1382.       " (16-bit, medium)",
  1383. #  else
  1384. #  if defined(M_I86CM) || defined(__COMPACT__)
  1385.       " (16-bit, compact)",
  1386. #  else
  1387. #  if defined(M_I86SM) || defined(__SMALL__)
  1388.       " (16-bit, small)",
  1389. #  else
  1390. #  if defined(M_I86TM) || defined(__TINY__)
  1391.       " (16-bit, tiny)",
  1392. #  else
  1393.       " (16-bit)",
  1394. #  endif
  1395. #  endif
  1396. #  endif
  1397. #  endif
  1398. #  endif
  1399. #  endif
  1400. #endif
  1401.  
  1402. #ifdef __DATE__
  1403.       " on ", __DATE__
  1404. #else
  1405.       "", ""
  1406. #endif
  1407.     );
  1408.  
  1409.     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
  1410.                                 /* MSC can't handle huge macro expansion */
  1411.  
  1412.     /* temporary debugging code for Borland compilers only */
  1413. #ifdef __TURBOC__
  1414.     Info(slide, 0, ((char *)slide, "\tdebug(__TURBOC__ = 0x%04x = %d)\n",
  1415.       __TURBOC__, __TURBOC__));
  1416. #ifdef __BORLANDC__
  1417.     Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ = 0x%04x)\n",
  1418.       __BORLANDC__));
  1419. #else
  1420.     Info(slide, 0, ((char *)slide, "\tdebug(__BORLANDC__ not defined)\n"));
  1421. #endif
  1422. #ifdef __TCPLUSPLUS__
  1423.     Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ = 0x%04x)\n",
  1424.       __TCPLUSPLUS__));
  1425. #else
  1426.     Info(slide, 0, ((char *)slide, "\tdebug(__TCPLUSPLUS__ not defined)\n"));
  1427. #endif
  1428. #ifdef __BCPLUSPLUS__
  1429.     Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ = 0x%04x)\n\n",
  1430.       __BCPLUSPLUS__));
  1431. #else
  1432.     Info(slide, 0, ((char *)slide, "\tdebug(__BCPLUSPLUS__ not defined)\n\n"));
  1433. #endif
  1434. #endif
  1435.  
  1436. } /* end function version() */
  1437.  
  1438. #endif /* !SFX */
  1439.  
  1440.  
  1441.  
  1442.  
  1443.  
  1444. #if (defined(__GO32__) || defined(__EMX__))
  1445.  
  1446. int volatile _doserrno;
  1447.  
  1448. unsigned _dos_getcountryinfo(void *countrybuffer)
  1449. {
  1450.     asm("movl %0, %%edx": : "g" (countrybuffer));
  1451.     asm("movl $0x3800, %eax");
  1452.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1453.     _doserrno = 0;
  1454.     asm("jnc 1f");
  1455.     asm("movl %%eax, %0": "=m" (_doserrno));
  1456.     asm("1:");
  1457.     return _doserrno;
  1458. }
  1459.  
  1460.  
  1461. #if (!defined(__DJGPP__) || (__DJGPP__ < 2))
  1462.  
  1463. void _dos_setftime(int fd, ush dosdate, ush dostime)
  1464. {
  1465.     asm("movl %0, %%ebx": : "g" (fd));
  1466.     asm("movl %0, %%ecx": : "g" (dostime));
  1467.     asm("movl %0, %%edx": : "g" (dosdate));
  1468.     asm("movl $0x5701, %eax");
  1469.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1470. }
  1471.  
  1472. void _dos_setfileattr(char *name, int attr)
  1473. {
  1474.     asm("movl %0, %%edx": : "g" (name));
  1475.     asm("movl %0, %%ecx": : "g" (attr));
  1476.     asm("movl $0x4301, %eax");
  1477.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1478. }
  1479.  
  1480. void _dos_getdrive(unsigned *d)
  1481. {
  1482.     asm("movl $0x1900, %eax");
  1483.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1484.     asm("xorb %ah, %ah");
  1485.     asm("incb %al");
  1486.     asm("movl %%eax, %0": "=a" (*d));
  1487. }
  1488.  
  1489. unsigned _dos_creat(char *path, unsigned attr, int *fd)
  1490. {
  1491.     asm("movl $0x3c00, %eax");
  1492.     asm("movl %0, %%edx": :"g" (path));
  1493.     asm("movl %0, %%ecx": :"g" (attr));
  1494.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1495.     asm("movl %%eax, %0": "=a" (*fd));
  1496.     _doserrno = 0;
  1497.     asm("jnc 1f");
  1498.     _doserrno = *fd;
  1499.     switch (_doserrno) {
  1500.     case 3:
  1501.            errno = ENOENT;
  1502.            break;
  1503.     case 4:
  1504.            errno = EMFILE;
  1505.            break;
  1506.     case 5:
  1507.            errno = EACCES;
  1508.            break;
  1509.     }
  1510.     asm("1:");
  1511.     return _doserrno;
  1512. }
  1513.  
  1514. unsigned _dos_close(int fd)
  1515. {
  1516.     asm("movl %0, %%ebx": : "g" (fd));
  1517.     asm("movl $0x3e00, %eax");
  1518.     asm("int $0x21": : : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi");
  1519.     _doserrno = 0;
  1520.     asm("jnc 1f");
  1521.     asm ("movl %%eax, %0": "=m" (_doserrno));
  1522.     if (_doserrno == 6) {
  1523.           errno = EBADF;
  1524.     }
  1525.     asm("1:");
  1526.     return _doserrno;
  1527. }
  1528.  
  1529. #endif /* !__DJGPP__ || (__DJGPP__ < 2) */
  1530.  
  1531.  
  1532. static int volumelabel(char *name)
  1533. {
  1534.     int fd;
  1535.  
  1536.     return _dos_creat(name, FA_LABEL, &fd) ? fd : _dos_close(fd);
  1537. }
  1538.  
  1539.  
  1540. #if (defined(__DJGPP__) && (__DJGPP__ > 1))
  1541.  
  1542. /* Prevent globbing of filenames.  This gives the same functionality as
  1543.  * "stubedit <program> globbing=no" did with DJGPP v1.
  1544.  */
  1545. int __crt0_glob_function(void)
  1546. {
  1547.     return 0;
  1548. }
  1549.  
  1550. /* Reduce the size of the executable and remove the functionality to read
  1551.  * the program's environment from whatever $DJGPP points to.
  1552.  */
  1553. void __crt_load_environment_file(void)
  1554. {
  1555. }
  1556.  
  1557. #endif /* __DJGPP__ > 1 */
  1558. #endif /* __GO32__ || __EMX__ */
  1559.